class StateList {
    constructor(list = [], key = 'key') {
        this.key = key;
        this.list = list;
        this.indexes = [];
        this.ready = false;
    }

    append(items) {
        this._checkAndInit();
        items.forEach((item) => {
            this.indexes.push(item[this.key]);
        });
        this.list.push.apply(this.list, items);
    }

    prepend(items) {
        this._checkAndInit();
        let indexes = [];
        items.forEach((item) => {
            indexes.push(item[this.key]);
        });
        this.indexes.unshift.apply(this.indexes, indexes);
        this.list.unshift.apply(this.list, items);
    }

    remove(key) {
        this._checkAndInit();
        let index = this.indexes.indexOf(key);
        if (index !== -1) {
            this.indexes.splice(index, 1);
            this.list.splice(index, 1);
        }
    }

    assign(item) {
        this._checkAndInit();
        let key = item[this.key], index = this.indexes.indexOf(key);
        if (index === -1) {
            this.indexes.push(key);
            this.list.push(item);
        } else {
            this.list.splice(index, 1, item);
        }
    }

    get(key) {
        this._checkAndInit();
        let index = this.indexes.indexOf(key);
        return this.list[index];
    }

    move(index, item) {
        this._checkAndInit();
        let key = item[this.key], idx = this.indexes.indexOf(key);
        if (index !== idx) {
            if (idx !== -1) {
                this.indexes.splice(idx, 1);
                this.list.splice(idx, 1);

                index = index > idx ? index - 1 : index;

                this.indexes.splice(index, 0, key);
                this.list.splice(index, 0, item);
            } else {
                console.log(index, key);
                this.indexes.splice(index, 0, key);
                this.list.splice(index, 0, item);
            }
        }
    }

    before(key, item) {
        this.move(this.indexes.indexOf(key), item);
    }

    after(key, item) {
        this.move(this.indexes.indexOf(key) + 1, item);
    }

    clear() {
        this.indexes.splice(0, this.indexes.length);
        this.list.splice(0, this.list.length);
    }

    _checkAndInit() {
        this.ready = true;
        this.list.forEach((value, index) => {
            this.indexes.push(value[this.key]);
        });
        this._checkAndInit = () => {};
    }

    forEach(fn) {
        // 避免重复循环
        if (this.ready) {
            this.list.forEach(fn);
        } else {
            this.ready = true;
            this.list.forEach((value, index) => {
                this.indexes.push(value[this.key]);
                fn(value, index);
            });
            this._checkAndInit = () => {};
        }
    }
}

function Data(value) {
    this.value = value;
    this.onchange = () => {};
}

function Index(val) {
    let value = val, listeners = [];
    this.valueOf = function () {
        return value;
    };
    this.toString = function () {
        return String(value);
    };
    this.setValue = function (val) {
        value = val;
        for (let listener of listeners) {
            listener();
        }
    };
    this.watch = function (listener) {
        listeners.push(listener);
    };
    this.clearWatch = function () {
        listeners = [];
    }
}

function render(dom, proto) {
    // 获取节点所属文档树
    let owner = dom.ownerDocument;
    // 创建虚拟节点用于最终渲染
    let fragment = owner.createDocumentFragment();
    // 用于监听动态数据变更
    let watches = new Map();
    // 用于存储被监听的数组数据
    let arrayWatches = [];

    // 用来监听数组
    function addArrayObserve(array, listener) {
        arrayWatches.push({array, listener});
        array.__ob__.add(listener);
    }
    proto.$refs = {};

    // 用于处理样式
    function style(styles) {
        let node = owner.createElement('style');
        node.innerHTML = styles;
        fragment.appendChild(node);
    }

    // 定义用于渲染节点的方法
    function element(tag, attrs, events, children, model = null) {
        let $model = null;
        // 处理 model
        if (model !== null) {
            let keys = model.split('.');
            $model = {target: proto.data, name: keys.pop(), expression: model};
            for (let key of keys) {
                if ($model.target.hasOwnProperty(key)) {
                    $model.target = $model.target[key];
                } else {
                    $model = null;
                    console.error('[ERROR] - 无法完成数据绑定，属性 "' + model + '" 未定义.');
                }
            }
        }
        // 创建节点对象
        let el = createElement(owner, tag, attrs, children, watch, $model, proto);
        // 处理事件
        for (let key in events) {
            let name = events[key];
            el[key] = (e) => {
                e.dom = el;
                return proto[name](e);
            };
        }
        // 处理路由跳转
        if (tag === 'page-link') {
            el.addEventListener('click', () => {
                dom.querySelectorAll('page-link').forEach((node) => {
                    node.removeAttribute('selected');
                });
                el.setAttribute('selected', 'selected');

                let pageView = dom.querySelector('page-view');
                const pt = require('path');
                const page = new (require(pt.resolve(proto.cwd(), el.getAttribute('to')) + '.js'))();
                page.section = attrs.section;
                if (pageView.router) {
                    pageView.router.switch(page);
                } else {
                    page.load(pageView);
                }
            }, true);
        } else if (tag === 'page-view') {
            // 处理默认路由
            const pt = require('path');
            const page = new (require(pt.resolve(proto.cwd(), el.getAttribute('src')) + '.js'))();
            page.load(el);
        }

        return el;
    }

    // 定义用于遍历渲染节点的方法
    function each(list, iterator) {
        let indexes = [], bench = new Text();

        if (typeof list.forEach !== 'function') {
            let container = owner.createDocumentFragment();
            for (let key in list) {
                let val = list[key];
                container.appendChild(iterator(val, key));
            }
            container.appendChild(bench);
            return container;
        } else {
            // 生成节点
            function generate(start, items) {
                let box = owner.createDocumentFragment();
                items.forEach((item, seq) => {
                    let idx = start + seq;
                    let index = new Index(idx);
                    indexes.splice(idx, 0, index);
                    box.appendChild(iterator(item, index));
                });
                return box;
            }

            let container = generate(0, list);
            // 用于管理循环界限
            let first = container.firstElementChild, amount = container.childElementCount;
            container.appendChild(bench);

            // 获取元素
            let find = (start) => {
                let cursor = 0;
                if (amount > 0 && start < amount) {
                    let node = first;
                    while (cursor < start) {
                        node = node.nextElementSibling;
                        cursor++;
                    }
                    return node;
                }
            };

            // 添加元素
            let add = (start, items) => {
                if (items.length > 0) {
                    let nodes = generate(start, items);

                    let target = find(start) || bench;
                    if (start === 0) first = nodes.firstElementChild;
                    amount += nodes.childElementCount;
                    target.before(nodes);
                    return true;
                }
            };

            // 删除元素
            let del = (start, count) => {
                count = Math.min(count, amount - start);
                if (count > 0) {
                    let target = find(start);
                    if (target) {
                        let cursor = 0;
                        while (cursor < count - 1) {
                            target.nextElementSibling.remove();
                            indexes[start + cursor].clearWatch();
                            cursor++;
                        }
                        amount -= count;
                        if (start === 0) first = amount > 0 ? target.nextElementSibling : null;
                        target.remove();
                        indexes[start + cursor].clearWatch();
                        indexes.splice(start, count);
                        return true;
                    }
                }
            };

            if (list instanceof StateList) {
                list = list.list;
            }

            function observeArray(array) {
                observe(array);

                function listener(method, args, len) {
                    switch (method) {
                        case 'push':
                            add(len, args);
                            break;
                        case 'pop':
                            del(len - 1, 1);
                            break;
                        case 'shift':
                            if (del(0, 1)) notify(0);
                            break;
                        case 'unshift':
                            if (add(0, args)) notify(args.length);
                            break;
                        case 'splice':
                            let [index, count, ...items] = args;
                            // 计算起点位置
                            index = index < 0 ? Math.max(0, len + index - 1) : Math.min(index, len);
                            // 计算删除长度
                            count = args.length < 2 ? len : (typeof count === 'number' ? count : 0);
                            // 先删除后添加
                            let changed = del(index, count) | add(index, items);
                            if (changed) notify(index);
                            break;
                        case 'reset':
                            array.__ob__.del(listener);
                            del(0, len);
                            add(0, args);
                            observeArray(args);
                            break;
                    }
                }

                addArrayObserve(array, listener);
            }

            observeArray(list);

            // 通知 index 进行变更
            const notify = (start) => {
                for (let i = start; i < indexes.length; i++) {
                    indexes[i].setValue(i);
                }
            };

            return container;
        }
    }

    // 用于遍历时获取动态内容
    function get(statement, variables = []) {
        let scope = {};
        for (let item of variables) {
            let names = item.split('.'), rootName = names.shift();
            if (proto.data.hasOwnProperty(rootName)) {
                let data = proto.data[rootName];
                let target = proto.data, key = rootName;
                scope[rootName] = data;
                while (names.length > 0) {
                    target = data;
                    key = names.shift();
                    data = data[key];
                }
                // 如果当前是数组，则需要进行监听
                if (Array.isArray(data) && !data.__ob__) {
                    observe(data);

                    // 监听整个数组对象的变更
                    let list = data;
                    Object.defineProperty(target, key, {
                        enumerable: true,
                        configurable: false,
                        set: (value) => {
                            if (list !== value) {
                                let original = list;
                                list = value;
                                original.__ob__.notify('reset', value, original.length);
                            }
                        },

                        get: () => {
                            return list;
                        }
                    });
                }
            } else {
                // 注册全局变量到 data 中
                scope[rootName] = global[rootName];
            }
        }
        return statement(scope);
    }

    // 定义用于显示动态内容的方法
    function data(statement, variables = [], scope = []) {
        let event;
        watch(variables, () => {
            let val = statement(proto.data);
            event.onchange(val == null ? '' : val);
        }, scope);
        let value = statement(proto.data);
        event = new Data(value == null ? '' : value);
        return event;
    }

    function text(statement, variables = [], scope = []) {
        let node;
        watch(variables, () => {
            let val = statement(proto.data);
            node.data = val == null ? '' : val;
        }, scope);
        let value = statement(proto.data);
        if (value instanceof DocumentFragment) return value;
        node = owner.createTextNode(value == null ? '' : value);
        return node;
    }

    // 监听动态数据
    function watch(variables, change, scope) {
        for (let variable of variables) {
            // 判断当前属性是否已经监听过
            let list = watches.get(variable);
            if (list) {
                list.push(change);
            } else {
                watches.set(variable, []);
                let keys = variable.split('.'), root = keys[0], name = keys.pop(), target = proto.data;
                // 防止全局变量混入
                if (target.hasOwnProperty(root)) {
                    for (let key of keys) {
                        target = target[key];
                    }
                    let val = target[name];

                    // 处理数组 length
                    if (Array.isArray(target) && name === 'length') {
                        observe(target);

                        target.__ob__.add((key, args, len) => {
                            if (len !== target.length) {
                                change();

                                // 通知更新
                                let list = watches.get(variable);
                                if (list) {
                                    for (let item of list) {
                                        item();
                                    }
                                }
                            }
                        });
                    } else if (Array.isArray(val)) {
                        observe(val);

                        val.__ob__.add(() => {
                            change();
                        });
                    } else {
                        Object.defineProperty(target, name, {
                            enumerable: true,
                            configurable: false,
                            set: (value) => {
                                if (val !== value) {
                                    val = value;

                                    // 立即更新首个被监听的
                                    change();

                                    // 通知更新
                                    let list = watches.get(variable);
                                    if (list) {
                                        for (let item of list) {
                                            item();
                                        }
                                    }
                                }
                            },

                            get: () => {
                                return val;
                            }
                        });
                    }
                } else {
                    // 注册全局变量到 data 中
                    Object.defineProperty(target, root, {
                        enumerable: false,
                        configurable: false,
                        get: () => global[root]
                    });
                }
            }
        }

        // 监听列表属性变更
        for (let entity of scope) {
            // 暂时只监听 index 变更
            if (entity instanceof Index) {
                entity.watch(change);
            }
        }
    }

    def(proto.data, '__ob__', {
        destroy() {
            // 清除常用的数据监听
            watches.clear();
            // 清除数组监听
            arrayWatches.forEach(({array, listener}) => {
                array.__ob__.del(listener);
            });
            arrayWatches = [];
        }
    });

    // 开始执行渲染方法
    proto.render((el) => {
        fragment.appendChild(el);
    }, style, element, text, data, each, get);

    // 添加到真实的文档树中
    dom.appendChild(fragment);
}

// 处理 model
function handelModel(tag, watch, model, el, custom) {
    // 处理 model
    if (model) {
        let {target, name, expression} = model;
        if (custom) {
            let change = () => {};
            // 针对自定义组件进行处理
            console.log(el);
            el.$model = {
                set value(val) {
                    target[name] = val;
                },

                get value() {
                    return target[name];
                },

                watch(cb) {
                    change = cb;
                },

                unwatch() {
                    change = () => {};
                }
            };

            watch([expression], () => {
                change(target[name]);
            }, []);
        } else {
            let type = tag === 'input' ? el.type : tag, value = target[name];
            if (type === 'checkbox' || type === 'radio') {
                if (Array.isArray(value) && type === 'checkbox') {
                    let list = observe(value);
                    list.__ob__.add(() => {
                        el.checked = list.indexOf(el.value) !== -1;
                    });
                    el.addEventListener('change', () => {
                        let val = el.value, idx = list.indexOf(val);
                        if (el.checked) {
                            if (idx === -1) list.push(val);
                        } else if (idx !== -1) {
                            list.splice(idx, 1);
                        }
                    }, true);

                    // 处理默认值
                    el.checked = list.indexOf(el.value) !== -1;
                } else if (typeof value === 'string' && type === 'radio') {
                    watch([expression], () => {
                        el.checked = target[name] === el.value;
                    }, []);
                    el.addEventListener('change', () => {
                        if (el.checked) {
                            target[name] = el.value;
                        }
                    }, true);

                    // 处理默认值
                    el.checked = value === el.value;
                } else {
                    watch([expression], () => {
                        el.checked = target[name];
                    }, []);
                    el.addEventListener('change', () => {
                        target[name] = el.checked;
                    }, true);

                    // 处理默认值
                    el.checked = value;
                }

            } else if (type !== 'button' && type !== 'submit' && type !== 'reset') {
                watch([expression], () => {
                    el.value = target[name];
                }, []);
                el.addEventListener('input', () => {
                    target[name] = el.value;
                }, true);
                // 处理默认值
                el.value = value;
            }
        }
    }
}

function createElement(owner, tag, attrs, children, watch, model, proto) {
    // 处理 component is
    if (tag === 'component') {
        tag = attrs.is;
        if (tag instanceof Data) {
            tag = tag.value;
        }
        delete attrs.is;
    }
    let ref = attrs['$ref'];
    if (ref) {
        delete attrs['$ref'];
    }

    let element;

    let gen = (iterator) => {
        for (let key in attrs) {
            let item = attrs[key], value = item;
            if (item instanceof Data) {
                item.onchange = (val) => {
                    iterator(key, val, true);
                };
                value = item.value;
            }
            iterator(key, value);
        }
    };

    // 判断是否是组件
    let Component = tag.indexOf('-') === -1 ? undefined : customElements.get(tag);
    if (Component === undefined) {
        element = owner.createElement(tag);
        gen((key, value) => element.setAttribute(key, value));

        // 处理子节点
        if (typeof children === 'function') {
            children = children();
        }

        for (let node of children) {
            element.append(node);
        }

        handelModel(tag, watch, model, element, false);
    } else {
        let attributes = {}, nodes = [];
        gen((key, value, changed) => {
            attributes[key] = value;
            if (typeof value === 'string') {
                if (changed) {
                    element.setAttribute(key, value);
                } else {
                    let node = owner.createAttribute(key);
                    node.value = value;
                    nodes.push(node);
                }
            }
        });
        element = new Component(attributes);
        element.$slot = children;
        nodes.forEach(attr => element.setAttributeNode(attr));
        nodes = [];

        handelModel(tag, watch, model, element, true);
    }

    if (typeof ref === 'string') {
        proto.$refs[ref] = element;
    }

    return element;
}

function observe(array) {
    if (!array.__ob__) {
        def(array, '__ob__', {
            watches: [],

            add(listener) {
                this.watches.push(listener);
            },

            notify(method, args, len) {
                for (let listener of this.watches) {
                    listener(method, args, len);
                }
            },

            del(listener) {
                for (let i = 0; i < this.watches.length; i++) {
                    if (listener === this.watches[i]) {
                        this.watches.splice(i, 1);
                        break;
                    }
                }
            }
        });
    }
    return array;
}

function def(target, key, value) {
    Object.defineProperty(target, key, {
        enumerable: false,
        configurable: true,
        value
    });
}

// 初始化数组
if (!Array.prototype.hasOwnProperty('__ob__')) {
    const methods = ['push', 'pop', 'shift', 'unshift', 'splice'];
    for (let key of methods) {
        let org = Array.prototype[key];
        def(Array.prototype, key, function (...args) {
            let ob = this.__ob__, len = this.length, result = org.apply(this, args);
            if (ob) ob.notify(key, args, len);
            return result;
        });
    }
    def(Array.prototype, '__ob__', null);
}

module.exports = {render, StateList};
